package org.faesa.masterSlave.master;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.faesa.masterSlave.common.DecodeText;
import org.faesa.masterSlave.common.SlaveId;

public class SlaveEntryPoint {
	private final static long INTERVAL_LIMIT = 10000;
	
	private static List<SlaveEntryPoint> slaveList;
	private static ScheduledExecutorService threadPool;
	private static ScheduledFuture<?> future;
	private SlaveId id;
	private DecodeText decodeText;
	private SlaveDecodeTextTask task;
	private long keepAlive;
	
	static {
		SlaveEntryPoint.slaveList = new ArrayList<SlaveEntryPoint>();
		SlaveEntryPoint.threadPool = Executors.newSingleThreadScheduledExecutor();
	}
	
	public SlaveEntryPoint(DecodeText decodeText) {
		this(new SlaveId(), decodeText);
	}
	public SlaveEntryPoint(SlaveId id, DecodeText decodeText) {
		if (id == null) {
			throw new NullPointerException("id");
		}
		if (decodeText == null) {
			throw new NullPointerException("decodeText");
		}
		this.id = id;
		this.decodeText = decodeText;
		this.task = new SlaveDecodeTextTask(this);
		this.keepAlive();
	}

	public DecodeText getDecodeText() {
		return decodeText;
	}
	public SlaveId getId() {
		return id;
	}
	public void executeTask() {
		this.task.executeMe();
	}
	public void cancelTask() {
		this.task.cancel();
	}
	public void finish() {
		this.task.cancel();
	}
	public boolean isAlive() {
		return (System.currentTimeMillis() - this.keepAlive) < INTERVAL_LIMIT;
	}
	private void keepAlive() {
		this.keepAlive = System.currentTimeMillis();
	}
	private synchronized static void schedule() {
		long minor = Long.MAX_VALUE;
		for (SlaveEntryPoint ep : SlaveEntryPoint.slaveList) {
			if (ep.keepAlive < minor) {
				minor = ep.keepAlive;
			}
		}
		long execTime = minor + INTERVAL_LIMIT;
		if (SlaveEntryPoint.future != null) {
			if (!SlaveEntryPoint.future.isDone() && !SlaveEntryPoint.future.isCancelled()){
				SlaveEntryPoint.future.cancel(false);
			}			
			SlaveEntryPoint.future = null;
		}
		
		if (execTime >= System.currentTimeMillis()) {
			future = SlaveEntryPoint.threadPool.schedule(new Runnable() {
				@Override
				public void run() {
					Iterator<SlaveEntryPoint> iter = SlaveEntryPoint.slaveList.iterator();
					while (iter.hasNext()) {
						SlaveEntryPoint ep = iter.next();
						if (!ep.isAlive()) {
							ep.finish();
							iter.remove();
						}
					}
					
					SlaveEntryPoint.schedule();
				}
				
			}, minor + INTERVAL_LIMIT, TimeUnit.MILLISECONDS);
		}		
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SlaveEntryPoint other = (SlaveEntryPoint) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
	
	public static synchronized SlaveEntryPoint register(DecodeText decodeText) {
		SlaveEntryPoint entryPoint = new SlaveEntryPoint(decodeText);
		SlaveEntryPoint.slaveList.add(entryPoint);
		SlaveEntryPoint.schedule();
		return entryPoint;
	}
	public static synchronized boolean unregister(SlaveId id) {
		Iterator<SlaveEntryPoint> iter = SlaveEntryPoint.slaveList.iterator();
		while (iter.hasNext()) {
			SlaveEntryPoint ep = iter.next();
			if (ep.getId().equals(id)) {
				iter.remove();
				SlaveEntryPoint.schedule();
				return true;
			}
		}
		return false;
	}
	public static synchronized SlaveEntryPoint get(SlaveId id) {
		for (SlaveEntryPoint ep : SlaveEntryPoint.slaveList) {
			if (ep.getId().equals(id)) {
				return ep;
			}
		}
		return null;
	}
	public static synchronized void keepAlive(SlaveId id) {
		SlaveEntryPoint.get(id).keepAlive();
		SlaveEntryPoint.schedule();
	}
}
